var CONST_DISTANCE = 10; // distance in km, talk about that
var geocoder = new google.maps.Geocoder();
var infoWindow = new google.maps.InfoWindow();
var infoWindowMarker = null;
var allMarkers = [];
var viewPort = null;
var dragStartCenter;
var map;

function createMap(element, options) {
	map = new google.maps.Map(element, options);
	var location = options.center;
	google.maps.event.addListener(map, 'center_changed', function() {
		var center = map.getCenter();
		var distance = distanceFrom(location.lat(), location.lng(), center
				.lat(), center.lng());
		if (distance > CONST_DISTANCE) {
			fetchMarkersAround(center.lat(), center.lng());
			location = center;
		}
	});
	google.maps.event.addListener(map, 'center_changed', function() {
		if (canPanTo(map.getCenter())) {
			dragStartCenter = map.getCenter();
			return;
		}
		map.panTo(dragStartCenter);
	});
	return map;
}

function canPanTo(point) {
	return viewPort == null || viewPort.contains(point);
}

function distanceFrom(lat1, lon1, lat2, lon2) {
	var R = 6371;
	var dLat = deg2rad(lat2 - lat1);
	var dLon = deg2rad(lon2 - lon1);
	var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(lat1))
			* Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
	return R * c; // distance in km
}

function deg2rad(deg) {
	return deg * Math.PI / 180;
}

function createDefaultMapOptions(centerPoint) {
	var options = {
		center : centerPoint,
		mapTypeId : google.maps.MapTypeId.ROADMAP,
		zoomControlOptions : {
			style : google.maps.ZoomControlStyle.SMALL,
			position : google.maps.ControlPosition.TOP_RIGHT
		},
		panControlOptions : {
			position : google.maps.ControlPosition.TOP_RIGHT
		},
		mapTypeControlOptions : {
			position : google.maps.ControlPosition.TOP_LEFT
		},
		zoom : 13,
		minZoom : 8,
		maxZoom : 15
	};
	return options;
}

function centerAtCoordinates(latitude, longitude) {
	centerAt(new google.maps.LatLng(latitude, longitude));
}

function convertResultToSearchResult(searchResults, results) {
	for (var i = 0; i < results.length; i++) {
		searchResults.push({
			federalStateId : findFederalStateId(results[i]),
			location : results[i].geometry.location,
			title : getFormattedAddress(results[i]),
		});
	}
}

function convertEventToSearchResult(searchResults, events) {
	for (var i = 0; i < events.length; i++) {
		searchResults.push({
			federalStateId : findFederalStateIdByName(events[i].federalState),
			location : new google.maps.LatLng(events[i].latitude, events[i].longitude),
			title : events[i].title,
		});
	}
}

function getValidResults(results) {
	var validResults = [];
	for (var i = 0; i < results.length; i++) {
		if (canPanTo(results[i].location))
			validResults.push(results[i]);
	}
	return validResults;
}

function showResultList(results, address) {
	setResultListVisible(true);
	document.getElementById("result-list-content").innerHTML = buildResultListContent(
			results, address);
}

function setResultListVisible(visible) {
	var resultList = document.getElementById("result-list");
	resultList.style.visibility = (visible) ? "visible" : "hidden";
}

function buildResultListContent(results, address) {
	if (results.length == 0) {
		// TODO: i18n
		return "Es konnten keine Ergebisse für " + address
				+ " gefunden werden!";
	}
	var result = "<ul>";
	for (var i = 0; i < results.length; i++) {
		var location = results[i].location;
		result += "<li style=\"list-style-image: url("
				+ getFaviconUrl(results[i].federalStateId) + ");\">";
		if (canPanTo(location))
			result += "<a href=\"javascript:void(0);\" onclick=\"centerResultAtCoordinates("
					+ location.lat()
					+ ","
					+ location.lng()
					+ ")\">"
					+ results[i].title + "</a>";
		else if (isAffiliate(results[i].federalStateId)) {
			result += "<a href=\"" + getWebsite(results[i].federalStateId)
					+ "\" >" + results[i].title + "</a>";
		} else {
			result += results[i].title;
		}
		result += "</li>";
	}
	result += "</ul>";
	return result;
}

function getFormattedAddress(result) {
	var address = "";
	var addressComponents = result['address_components'];
	for (var i = 0; i < addressComponents.length; i++) {
		var component = addressComponents[i]['long_name'];
		if (address.indexOf(component) == -1
				&& !isType(addressComponents[i], "postal_code")) {
			if (i > 0)
				address += ", ";
			address += addressComponents[i]['long_name'];
		}
	}
	return address;
}

function centerResultAtCoordinates(latitude, longitude) {
	setResultListVisible(false);
	centerAtCoordinates(latitude, longitude);
}

function centerAt(position) {
	if (canPanTo(position))
		map.panTo(position);
}

function fetchMarkersAround(latitude, longitude) {
	console.log("Fetching markers around " + latitude + ", " + longitude + "!");
	$.ajax({
		url : '../rest/locations',
		type : 'GET',
		data : 'latitude=' + latitude + "&longitude=" + longitude,
		dataType : 'json',
		success : function(markers) {
			var markerLength = markers.length;
			for (var i = 0; i < markerLength; i++) {
				var marker = markers[i];
				if (marker.hasOwnProperty('address'))
					addMarkerAtAddress(marker, marker.address);
				else if (marker.hasOwnProperty('latitude')
						&& marker.hasOwnProperty('longitude'))
					addMarkerAtCoordinates(marker, marker.latitude,
							marker.longitude);
				else
					console.error("Could not add marker: " + marker);
			}
		}
	});
}

function fetchMarkerTypes() {
	return [ "date", "pension", "restaurant", "stuff" ];
}

function toggleMarkerTypeCheckbox(checkbox) {
	var markerTypes = fetchMarkerTypes();
	for (var i = 0; i < markerTypes.length; i++) {
		var element = getMarkerTypeCheckbox(markerTypes[i]);
		if (element == checkbox) {
			toggleMarkerType(markerTypes[i], checkbox.checked);
			break;
		}
	}
}

function getMarkerTypeCheckbox(markerType) {
	return document.getElementById(markerType + "-checkbox");
}

function toggleMarkerType(markerType, checked) {
	for (var i = 0; i < allMarkers.length; i++) {
		if (allMarkers[i].icon.indexOf(markerType) > -1) {
			allMarkers[i].setVisible(checked);
			if (!checked && allMarkers[i] == infoWindowMarker)
				infoWindow.close();
		}
	}
}

function addMarkerAtAddress(markerDescription, address, description) {
	var input = {
		'address' : decodeURI(markerDescription.address)
	};
	geocoder.geocode(input, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {
			addMarkerAt(markerDescription, results[0].geometry.location);
		}
	});
}

function addMarkerAtCoordinates(markerDescription, latitude, longitude) {
	var point = new google.maps.LatLng(latitude, longitude);
	addMarkerAt(markerDescription, point);
}

function addMarkerAt(markerDescription, point) {
	if (containsMarkerDescription(markerDescription)) {
		return;
	}
	var marker = new google.maps.Marker({
		position : point,
		map : map,
		title : decodeURI(markerDescription.title),
		icon : 'gfx/marker_' + markerDescription.type + '.png'
	});
	marker.markerDescription = markerDescription;
	allMarkers.push(marker);
	google.maps.event.addListener(marker, 'click', function() {
		infoWindow.close();
		infoWindow.setContent("<div id=\"result-box\">"
				+ buildContent(markerDescription) + "</div>");
		infoWindowMarker = marker;
		infoWindow.open(map, marker);
	});
	// hide marker if checkbox is disabled
	var checkbox = getMarkerTypeCheckbox(markerDescription.type);
	if (checkbox != null)
		marker.setVisible(checkbox.checked);
}

function buildContent(markerDescription) {
	// {"id":"4887794144857229","title":"Ball- und Brauhaus
	// Watzke","description":"Restaurants und
	// Gastst\u00e4tten","latitude":"51.07756","longitude":"13.71582",
	// "type":"restaurant","street":"K\u00f6tzschenbroder Str.
	// 1","zip":"01139","city":"Dresden","distance":"3.3704579084574484"}
	var markerDescriptions = findSimilarDescriptions(markerDescription);
	if (markerDescriptions.length > 1)
		return buildContentFromDescriptions(markerDescriptions);
	return buildContentFromDescription(markerDescription);
}

function findSimilarDescriptions(markerDescription) {
	var result = [];
	for (var i = 0; i < allMarkers.length; i++) {
		if (allMarkers[i].markerDescription.latitude == markerDescription.latitude
				&& allMarkers[i].markerDescription.longitude == markerDescription.longitude) {
			result.push(allMarkers[i].markerDescription);
		}
	}
	return result;
}

function buildContentFromDescriptions(markerDescriptions) {
	var result = "";
	for (var i = 0; i < markerDescriptions.length; i++) {
		if (!markerDescriptions[i].hasOwnProperty('date')
				|| !markerDescriptions[i].date)
			result += buildContentFromDescription(markerDescriptions[i]);
	}
	result += "<table id=\"event-table\">";
	for (var i = 0; i < markerDescriptions.length; i++) {
		if (markerDescriptions[i].hasOwnProperty('date')
				&& markerDescriptions[i].date) {
			result += "<tr><td><b>" + decodeURI(markerDescriptions[i].title)
					+ "</b></td><td>";
			result += formatDateString(decodeURI(markerDescriptions[i].date));
			result += "</td></tr>";
		}
	}
	result += "</table>";
	return result;
}

function buildContentFromDescription(markerDescription) {
	var result = "<h3>" + decodeURI(markerDescription.title) + "</h3>";
	if (markerDescription.hasOwnProperty('description'))
		result += "<i>" + decodeURI(markerDescription.description)
				+ "</i></br></br>";

	if (markerDescription.hasOwnProperty('date') && markerDescription.date)
		result += "<b>" + formatDateString(decodeURI(markerDescription.date))
				+ "</b>";

	result += "<p>";
	if (markerDescription.hasOwnProperty('street') && markerDescription.street)
		result += decodeURI(markerDescription.street) + "</br>";
	if (markerDescription.hasOwnProperty('zip') && markerDescription.zip)
		result += decodeURI(markerDescription.zip);
	if (markerDescription.hasOwnProperty('city') && markerDescription.city)
		result += " " + decodeURI(markerDescription.city);
	result += "</p>";
	return result;
}

function formatDateString(string) {
	var date = new XDate(string);
	return date.toString("dd.MM.yyyy HH:mm");
}

function containsMarkerDescription(markerDescription) {
	for (var i = 0; i < allMarkers.length; i++) {
		if (allMarkers[i].markerDescription.id == markerDescription.id) {
			return true;
		}
	}
	return false;
}

function createBoundsForState($state) {
	geocoder.geocode({
		'address' : $state
	}, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {
			if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
				if (results && results[0] && results[0].geometry
						&& results[0].geometry.viewport) {
					viewPort = results[0].geometry.viewport;
				}
			}
		}
	});
}